package cn.maihe.elg.operation.centers.fuzhou.auth;

import cn.hutool.crypto.digest.MD5;
import cn.maihe.elg.operation.centers.fuzhou.config.FZMakeInfoConfig;
import cn.maihe.elg.operation.common.auth.Authorization;
import cn.maihe.elg.operation.config.ElgAuthConfig;
import cn.maihe.elg.operation.utils.ElgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.Assert;

import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.TreeSet;

/**
 * @Description 海迈签名验签算法
 * @Author WJH
 * @Date 2021/07/12
 */
@Slf4j
public class FZAuthorization implements Authorization {
    private final ElgAuthConfig elgAuthConfig;
    private final FZMakeInfoConfig.CenterInfo centerInfo;

    public FZAuthorization(ElgAuthConfig elgAuthConfig, FZMakeInfoConfig.CenterInfo centerInfo) {
        this.elgAuthConfig = elgAuthConfig;
        this.centerInfo = centerInfo;

        Assert.hasText(elgAuthConfig.getPubKey(), () -> "elg系统公钥不能为空");
        Assert.hasText(elgAuthConfig.getPriKey(), () -> "elg系统私钥不能为空");

        Assert.hasText(centerInfo.getAppId(), () -> centerInfo + "的appId不能为空");
        Assert.hasText(centerInfo.getSlat(), () -> centerInfo + "的slat不能为空");
        Assert.hasText(centerInfo.getSm2PriKey(), () -> centerInfo + "的sm2PriKey不能为空");
        Assert.hasText(centerInfo.getSm2PubKey(), () -> centerInfo + "的sm2PubKey不能为空");

    }

    @Override
    public String signature(Object body, String timestamp) {
        return null;
    }

    @Override
    public boolean verifySignature(Object reqBody, String signedMsg, String timestamp) {
        return false;
    }

    /**
     * 首先将请求加密参数（不需要URL编码，如果参数为null，需要转成字符串，sign本身不参加签名）按照字母升序排列，然后，将名称与值拼接在一起，
     * 接着将api_secret拼接在上一步得到的字符串两边，最后使用md5加密并转化成大写。最终得到的值即为请求加密参数中sign的值
     *
     * @param clearBizReqJsonBody
     * @return
     */
    public String signature(String clearBizReqJsonBody) {
        // 对请求报文做排序
        JSONObject requestJson = JSONObject.parseObject(clearBizReqJsonBody);
        String source = recursiveKeySort(requestJson);
        String messageDigest = this.buildMessageDigest(source);
        ElgLogger.debug(log, l -> l.debug("待签名字符串：{}", messageDigest));
        String signed = MD5.create().digestHex(messageDigest).toUpperCase();
        ElgLogger.debug(log, l -> l.debug("签名后字符串：{}", signed));

        return signed;
    }

    public boolean verifySignature(String clearBizReqJsonBody) {

        // 对请求报文做排序
        JSONObject requestJson = JSONObject.parseObject(clearBizReqJsonBody);
        Assert.notNull(requestJson.get("sign"), () -> "签名参数sign不能为空");
        String signedMsg = String.valueOf(requestJson.get("sign"));
        String source = recursiveKeySort(requestJson);
        String messageDigest = this.buildMessageDigest(source);
        ElgLogger.debug(log, l -> l.debug("签名后字符串：{}", messageDigest));
        String signed = MD5.create().digestHex(messageDigest).toUpperCase();
        ElgLogger.debug(log, l -> l.debug("签名摘要信息：{}", signed));
        boolean verifyTF = signed.equalsIgnoreCase(signedMsg);
        ElgLogger.debug(log, l -> l.debug("验签结果：[{}]", verifyTF));

        return verifyTF;
    }
    /**
     * 把所有参数名和参数值串在一起, 排除sign字段
     * @param json
     * @return
     */
    private static String recursiveKeySort(JSONObject json) {
        StringBuilder appender = new StringBuilder();
        Iterator<String> keys = new TreeSet<>(json.keySet()).iterator();
        while (keys.hasNext()) {
            String key = keys.next();
            if (!("sign").equals(key)) {
                String val = String.valueOf(json.get(key));
                appender.append(key).append(val);
            }
        }
        return appender.toString();
    }

    /**
     * 把api_secret的值夹在字符串的两端
     * @param clearBizReqJsonBody
     * @return
     */
    String buildMessageDigest(String clearBizReqJsonBody) {
        return String.format("%s%s%s", centerInfo.getApiSecret(), clearBizReqJsonBody, centerInfo.getApiSecret());
    }
}
